package com.key.w8.http1.endpoint;

import com.key.w8.http1.connection.Connection;
import com.key.w8.http1.connection.Http1ConnectionInitializer;
import com.key.w8.http1.connection.environment.Http1Environment;
import com.key.w8.http1.handler.Http1FullHandlerBuilder;
import com.key.w8.http1.handler.codec.CodecBuilder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

import static com.key.w8.http1.constant.TSLProtocol.TSL_V2;

/**
 * @Author: k8
 * @CreateTime: 2025-03-08
 * @Version: 1.0
 */
public class HServer {
    private Http1Environment environment;

    private ServerBootstrap serverBootstrap;
    private List<Connection> safeConnectionList;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workGroup;
    private Channel serverSocketChannel;
    public static final int DEFAULT_PORT = 4399;
    private int port = DEFAULT_PORT;
    public static final boolean DEFAULT_SSL_ENABLE = false;  // 默认不启用SSL
    private boolean sslEnable = DEFAULT_SSL_ENABLE;           // 是否启用 SSL
    private String tslProtocol = TSL_V2;

    public HServer setPort(int port) {
        this.port = port;
        return this;
    }
    public HServer setSslEnable(boolean sslEnable){
        this.sslEnable = sslEnable;
        return this;
    }
    public HServer setTslProtocol(String tslProtocol){
        this.tslProtocol = tslProtocol;
        return this;
    }

    public HServer(Http1Environment environment, ServerConnectionBuilder serverConnectionBuilder, CodecBuilder codecBuilder) {
        this.environment = environment;

        this.safeConnectionList = Collections.synchronizedList(new LinkedList<>());

        serverConnectionBuilder.addCreatedListener((connection -> {
            this.safeConnectionList.add(connection);
        }));
        serverConnectionBuilder.addCloseListener((connection)->{
            this.safeConnectionList.remove(connection);
        });
        Http1FullHandlerBuilder handlerBuilder = new Http1FullHandlerBuilder(codecBuilder,environment.isKeepAlive());
        handlerBuilder
                .setServer(environment.isServer())
                .setHeartbeatInterval(environment.getHeartbeatInterval())
                .setConnectionTimeout(environment.getConnectionTimeout())
                .setMaxContentLength(environment.getMaxContentLength())
                .setCompressResponseContent(environment.isCompressResponseContent());

        this.serverBootstrap = new ServerBootstrap();
        this.bossGroup = new NioEventLoopGroup(1);
        this.workGroup = new NioEventLoopGroup(environment.getMaxThreads());
        serverBootstrap.group(bossGroup,workGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new Http1ConnectionInitializer(serverConnectionBuilder,handlerBuilder,sslEnable
                        ,tslProtocol,environment.getCrtPath(),environment.getPrivateKeyPath(),environment.isServer()));
    }


    public CompletableFuture<Void> openServer(){
        ChannelFuture bind = serverBootstrap.bind(new InetSocketAddress(port));
        this.serverSocketChannel = bind.channel();
        CompletableFuture<Void> result = new CompletableFuture<>();
        bind.addListener(future -> {
            try {
                future.get();
                result.complete(null);
            }catch (Throwable throwable){
                result.completeExceptionally(throwable);
            }
        });
        return result;
    }


    public void shutDown() throws IOException {
        this.serverSocketChannel.close();//拒绝建立新连接
        List<Connection> tempCs = new ArrayList<>(safeConnectionList);
        for (Connection connection : tempCs) {
            connection.close();
        }
        try {
            Future<?> future = bossGroup.shutdownGracefully();
            future.get();
        }catch (Throwable throwable){
        }
        try {
            Future<?> future1 = workGroup.shutdownGracefully();
            future1.get();
        }catch (Throwable throwable){}

    }
}
